import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Calendar;

/*
 * Jason Lee (blujay)
 * EECS 382
 * P1A
 * SubmitServer.java
 */

class AcceptSubmit implements Runnable
{
	// an instance of submission acceptance
	
	public String usrName;
	public int numFiles;
	public ArrayList<String> fileNames;
	public ArrayList<Long> fileLengths;
	public boolean completed;
	private Socket s; 
	
	public AcceptSubmit(Socket acceptSocket) throws IOException
	{
		// REQUIRES: socket
		// MODIFIES: this
		// EFFECTS: creates an instance of AcceptSubmit
		
		this.completed = false;
		this.s = acceptSocket;
	}
	
	public AcceptSubmit getSubmit()
	{
		// REQUIRES: well-formed AcceptSubmit
		// MODIFIES: n/a
		// EFFECTS: returns the current instance of AcceptSubmit
		
		return this;
	}
	
	public void failed()
	{
		// REQUIRES: n/a
		// MODIFIES: n/a
		// EFFECTS: default error output
		
		System.err.println("Invalid connection.");
		System.exit(1);		
	}
	
	public String readStr(DataInputStream in)
	{
		// REQUIRES: instantiated DataInputStream
		// MODIFIES: n/a
		// EFFECTS: returns the string read from the stream (buffered)
		
		String tempString = null;
		char tempChar;

		try {
			while (true)
			{
				tempChar = in.readChar();
				if (tempChar == '\0') {
					break;
				}
				tempString.concat(String.valueOf(tempChar));
			}
		} catch (IOException e) {
			failed();
		}
		
		return tempString;
	}
	
	public void createDirectory(String user) throws IOException
	{
		// REQUIRES: user name
		// MODIFIES: user name string
		// EFFECTS: creates a directory
		
		Calendar thisTime = Calendar.getInstance();
		String dir = user + "."
				+ String.format("%02d", thisTime.get(Calendar.MONTH)+1)
				+ String.format("%02d", thisTime.get(Calendar.DAY_OF_MONTH))
				+ String.format("%02d", thisTime.get(Calendar.HOUR_OF_DAY))
				+ String.format("%02d", thisTime.get(Calendar.MINUTE))
				+ String.format("%02d", thisTime.get(Calendar.SECOND));
		File dirPath = new File(dir);
		if (!dirPath.mkdir())
		{
			throw new IOException();
		}
		System.out.println("### Created directory " + dir);
	}
	
	
	public void IO()
	{
		// REQUIRES: a good connection
		// MODIFIES: this
		// EFFECTS: reads submission from data stream, 
		//			writes stuff to a file in directory
		
		DataInputStream in = null;
		char tempChar;
		try {
			in = new DataInputStream(s.getInputStream());
		} catch (IOException e) {
			failed();
		}
		
		// read name
		usrName = readStr(in);
		
		// TODO: add create directory
		try{
			createDirectory(usrName);
		} catch (IOException e) {
			failed();
		}
		
		// read number of files
		try {
			numFiles = in.readInt();
		} catch (IOException e) {
			failed();		
		}
		
		// read file names, lengths, and contents
		for (int j = 0; j < 3; j++)
		{
			for (int i = 0; i < numFiles; i++)
			{
				switch(j) {
					case 0: // rd file name
						fileNames.add(readStr(in));
						break;
					case 1: // rd file lengths
						try {
							fileLengths.add(in.readLong());
						} catch (IOException e) {
							failed();
						}
						break;
					case 2: // rd and write file contents
						FileWriter fstream = null;
						try {
							fstream = new FileWriter(fileNames.get(i));
							BufferedWriter out = new BufferedWriter(fstream);
							long bytLength = fileLengths.get(i);
							for(long k = 0; k < bytLength; k++)
							{
							 	out.write(in.readByte());
							}
							out.close();	 
						} catch (IOException e) {
							failed();
						}
					break;
				}
			}
		}
		
		FileWriter finalStream = null;
		try {
			finalStream = new FileWriter("SUBMIT_DONE");
			BufferedWriter finalOut = new BufferedWriter(finalStream);
			finalOut.close();
		} catch (IOException e) {
			failed();
		}
	}
	
	public void debug()
	{
		// Debug output
		
		System.out.println("### Getting submission from user name: " + usrName);
		System.out.println("### Created directory: ");
		System.out.println("### Expecting " + numFiles + "files");
		System.out.println("### File names and size:");
		for(int i = 0; i < numFiles; i++)
		{
			System.out.println("### File " + i + ": " + fileNames.get(i) + " Size: " + fileLengths.get(i));
		}
	}
	
	public void run()
	{
		// reimplementation of runnable interface method
		
		IO();
		debug();
		completed = true;
	}
}

public class SubmitServer {
	
	// main server class
	
	public static int portAddr;
	public byte userProvided;
	
	public static void main (String [] args) throws IOException, NumberFormatException
	{
		// main fxn
		
		ServerSocket ss = null;
		if(args.length > 0)
		{
			try {
				portAddr = Integer.parseInt(args[0]);
				ss = new ServerSocket(portAddr);
			} catch (NumberFormatException e) {
				System.out.println("Invalid port number.");
	            System.exit(1);				
			}
		} else {
			ss = new ServerSocket(0);
			portAddr = ss.getLocalPort();
		}
		System.out.println("Port: " + portAddr);
		ss.setSoTimeout(15000);
		while(true)
		{
			try {
				Socket acceptSocket = ss.accept();
				AcceptSubmit tempAccept = new AcceptSubmit(acceptSocket);
				AcceptSubmit thisAccept = tempAccept.getSubmit(); // redundant?
				Thread thisThread = new Thread(thisAccept);
				if(!thisAccept.completed)
				{
					thisThread.start();
					thisThread.join();
				}
				if(thisAccept.completed || !thisThread.isAlive())
					ss.close();
			} catch (IOException e) {
				System.out.println("Invalid port connection or not connected");
				System.exit(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				System.exit(1);
			}
		}
	}
	
}
